package org.example.mallchat.websocket;

import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;
import org.example.mallchat.websocket.domain.enums.WSReqTypeEnum;
import org.example.mallchat.websocket.domain.vo.request.WSBaseReq;
import org.example.mallchat.websocket.service.WebSocketService;

/**
 * @author: zzh
 * @date: 2024/12/18 09:44:39
 * @version: 1.0
 */
@ChannelHandler.Sharable
@Slf4j
public class NettyWebSocketServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    private WebSocketService webSocketService;// 引入websocket服务 ，专门处理channel和用户的关系存储Map

    /**
     * 连接成功触发方法
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        webSocketService = SpringUtil.getBean(WebSocketService.class);
        // 连接成功，将channel存入websocket服务Map中
        webSocketService.connectChannel(ctx.channel());
        //不向下传递了，没有必要！
        //super.channelActive(ctx);
    }


    /**
     * 连接断开触发方法 正常关闭连接
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        offline(ctx);
    }


    /**
     * 心跳检查 异常关闭连接
     * @param ctx
     * @param evt
     * @throws Exception
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        // 心跳检查
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
            // 读空闲
            if (idleStateEvent.state() == IdleState.READER_IDLE) {
                // 关闭连接
                offline(ctx);
            }
        }else if (evt instanceof WebSocketServerProtocolHandler.HandshakeComplete) {
            // 握手认证事件
            // 取出token 进行认证
            String token = (String) ctx.attr(AttributeKey.valueOf("token")).get();
            webSocketService.authorize(ctx.channel(), token);
        }
        super.userEventTriggered(ctx, evt);
    }


    /**
     * 处理websocket请求
     * @param channelHandlerContext
     * @param textWebSocketFrame
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) throws Exception {
        // 获取客户端发送的消息
        String text = textWebSocketFrame.text();
        // 格式转换json 转成自定义的消息对象
        WSBaseReq wsBaseReq = JSONUtil.toBean(text, WSBaseReq.class);
        WSReqTypeEnum wsReqTypeEnum = WSReqTypeEnum.of(wsBaseReq.getType());
        switch (wsReqTypeEnum) {
            case LOGIN:
                log.info("请求二维码 = " + text);
                // 处理登录请求
                webSocketService.handleLoginTicket(channelHandlerContext.channel());
                break;
            case HEARTBEAT:
                break;
            case AUTHORIZE:
                // 登录认证 (基于已经登录存在token的前提下！)
                webSocketService.authorize(channelHandlerContext.channel(), wsBaseReq.getData());
                break;
            default:
                log.info("未知类型");
        }
    }

    /**
     * 用户下线统一处理
     */
    private void offline(ChannelHandlerContext ctx) {
        // 移除用户的连接
        webSocketService.disconnectChannel(ctx.channel());
        // 关闭用户的连接
        ctx.channel().close();
    }


}
